package com.earogya.springsecurity.security;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.authentication.dao.SaltSource;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
import com.earogya.springsecurity.security.SaltedUser;

/**
 * Extends the baseline Spring Security JdbcDaoImpl and implements required
 * functionalities.
 * 
 * @author Ureka
 */
public class CustomJdbcDaoImpl extends JdbcDaoImpl implements
		ChangePasswordInterface, CreateUsersInterface,
		AddUsersToUserGroupsInterface {
	@Autowired
	private PasswordEncoder passwordEncoder;
	@Autowired
	private SaltSource saltSource;

	/**
	 * 
	 * Implement the change password functionality
	 * 
	 * @param username
	 * @param newPassword
	 * @param old password
	 * 
	 * @return redirect url
	 */
	public String changePassword(String username, String newPassword,String oldPassword) {
		String error = "";
		UserDetails user = loadUserByUsername(username);
		if (passwordEncoder.encodePassword(oldPassword,saltSource.getSalt(user)).equals(user.getPassword())) {
			String encodedPassword = passwordEncoder.encodePassword(newPassword, saltSource.getSalt(user));
			getJdbcTemplate().update("UPDATE USERS SET PASSWORD = ? WHERE USERNAME = ?",encodedPassword, username);
		} else {
			error = "Incorrect old password";
		}
		return error;
	}

	/**
	 * 
	 * Create UserDetails object
	 * @param username
	 * @param userFromUserQuery
	 * @param combinedAuthorities
	 * 
	 * @return SaltedUser 
	 * 
	 */
	protected UserDetails createUserDetails(String username,
			UserDetails userFromUserQuery,
			List<GrantedAuthority> combinedAuthorities) {
		String returnUsername = userFromUserQuery.getUsername();

		if (!isUsernameBasedPrimaryKey()) {
			returnUsername = username;
		}

		return new SaltedUser(returnUsername,
				((SaltedUser) userFromUserQuery).getName(),
				userFromUserQuery.getPassword(),
				((SaltedUser) userFromUserQuery).getEmail(),
				((SaltedUser) userFromUserQuery).getDepartment(),
				userFromUserQuery.isEnabled(), true, true, true,
				combinedAuthorities, ((SaltedUser) userFromUserQuery).getSalt());
	}

	
	/**
	 * 
	 * Load the users based on the username
	 * 
	 * @param username
	 */
	@Override
	protected List<UserDetails> loadUsersByUsername(String username) {
		return getJdbcTemplate().query(getUsersByUsernameQuery(),
				new String[] { username }, new RowMapper<UserDetails>() {
					public UserDetails mapRow(ResultSet rs, int rowNum)
							throws SQLException {
						String username = rs.getString(1);
						String name = rs.getString(2);
						String password = rs.getString(3);
						String email = rs.getString(4);
						String department = rs.getString(5);
						boolean enabled = rs.getBoolean(6);
						String salt = rs.getString(7);
						return new SaltedUser(username, name, password, email,
								department, enabled, true, true, true,
								AuthorityUtils.NO_AUTHORITIES, salt);
					}
				});
	}

	
	/**
	 * 
	 * Create a user and save to the database
	 * 
	 * @param username
	 * @param name
	 * @param password
	 * @param email
	 * @param department
	 * 
	 * throws  DuplicateKeyException
	 * 
	 */
	public void createUser(String username, String name, String password,
			String email, String department) throws DuplicateKeyException {
		String salt = String.valueOf(Math.random() * 1000000000);
		String encodedPassword = passwordEncoder.encodePassword(password, salt);
		getJdbcTemplate().update("INSERT INTO USERS VALUES (?,?,?,?,?,true,?)",
				username, name, encodedPassword, email, department, salt);
	}

	
	/**
	 * 
	 * Add the users to the user groups
	 * 
	 * @param username
	 * @param userGroup
	 * 
	 * @return isSuccess
	 * 
	 */
	public Boolean addUsersToUserGroups(String username, int userGroup) {
		Boolean isSuccess = false;
		try {
			int count = getJdbcTemplate().queryForInt(
					"SELECT count(*) FROM GROUP_MEMBERS WHERE USERNAME=?",
					new Object[] { username });
			int usersCount = getJdbcTemplate().queryForInt(
					"SELECT count(*) FROM USERS WHERE USERNAME=?",
					new Object[] { username });
			if (usersCount == 0) {
				isSuccess = false;
			} else {
				if (count == 0) {
					getJdbcTemplate().update("INSERT INTO GROUP_MEMBERS(username,group_id) VALUES (?,?)",username, userGroup);
				} else {
					getJdbcTemplate().update("UPDATE GROUP_MEMBERS SET group_id=? WHERE USERNAME=?",userGroup, username);
				}
				isSuccess = true;
			}
		} catch (Exception e) {
			isSuccess = false;
		}
		return isSuccess;
	}
}
